package pers.li.utils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;

public class DataProcess {

    //类型参数的集合
    static Set<String> typeSet;
    private boolean camelCase;

    public DataProcess(boolean camelCase) {
        this.camelCase = camelCase;
    }

    public DataProcess() {
    }

    static {
        String[] types = {"byte", "short", "int", "char", "double", "float", "long", "java.lang.String", "java.util.Date"};
        typeSet = new HashSet<>(Arrays.asList(types));
    }

    private String transitionCamelCase(String str) {
        StringBuilder result = new StringBuilder();
        char[] chars = str.toCharArray();
        int length = chars.length;
        for (int i = 0; i < length - 1; i++) {
            if (chars[i + 1] >= 'A' && chars[i + 1] <= 'Z') {
                chars[i + 1] += 32;
                result.append(chars[i]);
                result.append("_");
            } else
                result.append(chars[i]);
        }
        result.append(chars[length - 1]);
        return result.toString();
    }

    private String toCamelCase(String str) {
        StringBuilder result = new StringBuilder();
        if (str.contains("_")) {
            String[] strings = str.split("_");
            result.append(strings[0]);
            for (int i = 1; i < strings.length; i++) {
                char[] chars = strings[i].toCharArray();
                if (chars[0] >= 'a' && chars[0] <= 'z') {
                    chars[0] -= 32;
                }
                result.append(chars);
            }
            return result.toString();
        }
        return str;
    }

    /**
     * 获取执行sql语句后的结果集
     *
     * @param sql        sql语句
     * @param connection 连接
     * @param args       sql语句中的参数
     * @return 执行sql语句后的结果集
     */
    public ResultSet getResultSet(String sql, Connection connection, Object... args) {
        try {
            assert connection != null;
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            Object[] clone = args.clone();
            for (int i = 0; i < clone.length; i++) {
                preparedStatement.setObject(i + 1, clone[i]);
            }
            return preparedStatement.executeQuery();
        } catch (SQLException throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    /**
     * 将执行sql语句后的结果集赋值给相应对象的属性
     * resultMapper解决sql语句的列与bean对象属性名称不对应的问题
     * resultMapper的key值为对象属性名，value为映射的sql列名
     *
     * @param resultSet    执行sql语句获取的结果集
     * @param resultType   对象的类型
     * @param resultMapper 对象的属性名和sql语句列名的映射集合
     */
    private <T> List<T> resultSetToObjectList(ResultSet resultSet, Class<T> resultType, Map<String, String> resultMapper) throws SQLException, IllegalAccessException, InstantiationException {
        List<T> resultList = new ArrayList<>();
        Field[] fields = resultType.getDeclaredFields();
        ResultSetMetaData metaData = resultSet.getMetaData();
        int columnCount = metaData.getColumnCount();
        Set<String> hashSet = new HashSet<>();
        for (int i = 0; i < columnCount; i++) {
            hashSet.add(metaData.getColumnName(i + 1));
        }
        while (resultSet.next()) {
            T object = resultType.newInstance();
            for (Field field : fields) {
                field.setAccessible(true);
                String property = camelCase ? transitionCamelCase(field.getName()) : field.getName();
                if (hashSet.contains(property))
                    field.set(object, resultSet.getObject(property));
                else if (resultMapper != null && resultMapper.containsKey(field.getName())) {
                    field.set(object, resultSet.getObject(resultMapper.get(field.getName())));
                }
            }
            resultList.add(object);
        }
        resultSet.close();

        return resultList.size() == 0 ? null : resultList;
    }

    public <T> List<T> getObjectList(String sql, Class<T> resultType, Map<String, String> resultMapper, Object... args) {
        Connection connection = JDBCTool.getConnection();
        assert connection != null;
        ResultSet resultSet = getResultSet(sql, connection, args);
        try {
            if (typeSet.contains(resultType.getName())) {
                List<T> resultList = new ArrayList<>();
                while (resultSet.next()) {
                    resultList.add(resultSet.getObject(1, resultType));
                }
                if (resultList.size() == 0)
                    return null;
                else
                    return resultList;
            } else {
                if (resultSet != null) {
                    try {
                        return resultSetToObjectList(resultSet, resultType, resultMapper);
                    } catch (IllegalAccessException | InstantiationException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (SQLException exception) {
            exception.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return null;
    }

    public <T> List<T> getObjectList(String sql, Class<T> resultType, Object... args) {
        return getObjectList(sql, resultType, null, args);
    }

    public <T> T getObject(String sql, Class<T> resultType, Object... args) {
        List<T> objectList = getObjectList(sql, resultType, args);
        return objectList == null ? null : objectList.get(0);
    }

    public <T> T getObject(String sql, Class<T> resultType, Map<String, String> resultMapper, Object... args) {
        return getObjectList(sql, resultType, resultMapper, args).get(0);
    }

    /**
     * 将sql查询的结果映射成map
     *
     * @param sql  要执行的sql语句
     * @param args 设置sql语句的所需要的参数
     * @return 返回的map集合
     */
    public Map<String, ?> getMapper(String sql, Object... args) {
        Connection connection = JDBCTool.getConnection();
        Map<String, Object> resultMap = new HashMap<>();
        assert connection != null;
        ResultSet resultSet = getResultSet(sql, connection, args);
        try {
            ResultSetMetaData metaData = resultSet.getMetaData();
            while (resultSet.next()) {
                int columnCount = metaData.getColumnCount();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = camelCase ? toCamelCase(metaData.getColumnName(i)) : metaData.getColumnName(i);
                    resultMap.put(columnName, resultSet.getObject(i));
                }
            }
            return resultMap;
        } catch (SQLException exception) {
            exception.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException exception) {
                exception.printStackTrace();
            }
        }
        return null;
    }

    public int updateRow(String sql, Object... args) {
        Connection connection = JDBCTool.getConnection();
        try {
            assert connection != null;
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            Object[] clone = args.clone();
            int index = 1;
            for (Object obj : clone) {
                preparedStatement.setObject(index, obj);
                index++;
            }
            if (preparedStatement.executeUpdate() != 0) {
                sql = "SELECT LAST_INSERT_ID()";
                PreparedStatement preparedStatement1 = connection.prepareStatement(sql);
                ResultSet resultSet = preparedStatement1.executeQuery();
                while (resultSet.next()) {
                    return resultSet.getInt(1);
                }
            }


        } catch (SQLException exception) {
            exception.printStackTrace();
        } finally {
            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException exception) {
                exception.printStackTrace();
            }
        }
        return -1;
    }

    public int deleteRow(String sql, Object... args) {
        return updateRow(sql, args);
    }

    public int insertRow(String sql, Object... args) {
        return updateRow(sql, args);
    }


}
